#include <pspkernel.h>
#include "pspiofilemgr_kernel2.h"

int NP9660IoInit(PspIoDrvArg* arg);
int NP9660IoExit(PspIoDrvArg* arg);
int NP9660IoOpen(PspIoDrvFileArg* arg, char* filename, int flags, SceMode mode);
int NP9660IoClose(PspIoDrvFileArg* arg);
int NP9660IoRead(PspIoDrvFileArg* arg, void* outdata, SceSize size);
SceOff NP9660IoLseek(PspIoDrvFileArg* arg, int a1, SceOff offset, int a3, int from);
int NP9660IoIoCtl(PspIoDrvFileArg* arg, unsigned int cmd, void* indata, int inlen, void* outdata, int outlen);
int NP9660IoDevCtl(PspIoDrvFileArg* arg, const char* devname, unsigned int cmd, void* indata, int inlen, void* outdata, int outlen);

PspIoDrvFuncs np9660_funcs = { // 0x000086D4
	NP9660IoInit, // 0x00003CF4
	NP9660IoExit, // 0x00003ED4
	NP9660IoOpen, // 0x00003FA4
	NP9660IoClose, // 0x000040A4
	(void*)NP9660IoRead, // 0x00004268
	(void*)NULL,
	(void*)NP9660IoLseek, // 0x00004110
	NP9660IoIoCtl, // 0x00004A2C
	(void*)NULL,
	(void*)NULL,
	(void*)NULL,
	(void*)NULL,
	(void*)NULL,
	(void*)NULL,
	(void*)NULL,
	(void*)NULL,
	(void*)NULL,
	(void*)NULL,
	(void*)NULL,
	(void*)NULL,
	NP9660IoDevCtl, // 0x00005228
	(void*)NULL
};
PspIoDrv np9660_driver = { "umd", 0x04, 0x800, "NP9660", &np9660_funcs }; // 0x0000872C

typedef struct File {
	int opened;
	int unk1; // offset ?
}
File files[8]; // 476

struct unk_i100 { // 100 // read_params ?
	int read_size;
	void* buffer; // max_size: 512
}

SceUID Np9660Mutex = 0; // 368
SceUID Np9660NCMutex = 0; // 372
SceUID Np9660Mutex2 = 0; // 376
SceUID Np9660Fpl = 0; // 380
void* fpl_buffer = NULL; // 384

struct unk_i544 { // 544
	int unk0;
	int unk1;
};
void* sectors = NULL; // 560 // read buffer for first logical block ? max_size: 0x8000 (10*SECTOR_SIZE)
int read_size = 0; // 564 // Read size for first logical block ?

SceUID SceNp9660Dec = 0; // 2680
SceUID SceNp9660DecMbx = 0; // 2684
SceUID SceNp9660DecEventFlag = 0; // 2688

SceInt64 time; // 4696 & 4700

// OK
// Maybe SetReadBuffer(buffer, size)
// 0x00000DA8
int sub_00000DA8(void* buffer, int size){
	if(size != 512){
		return SCE_ERROR_ERRNO_EINVAL;
	}

	unk_i100.buffer = buffer;
	unk_i100.read_size = -1;

	return 0;
}

// OK
// 0x00000DD8
int sub_00000DD8(){
	return 0;
}

// OK
// Maybe SetReadBufferForSectorsReading(buffer, size)
// 0x00003C88
int sub_00005130(void* buf, int size){
	if(size != 0x8000){
		return SCE_ERROR_ERRNO_EINVAL;
	}

	sectors = buf; // read buffer ?
	read_size = -1;   // read size ?
	return 0;
}

// Ok
// 0x00003CF4
int NP9660IoInit(PspIoDrvArg* arg){
	int ret_error = 0;
	int ret = 0;
	memset(unk_i388, 0, 88);

	Np9660Fpl = sceKernelCreateFpl("SceNp9660", 1, 0x100, 32*0x800+0x200, 1, 0);
	if(Np9660Fpl < 0){
		ret_error = Np9660Fpl;
		goto error;
	}
	
	void* buf = NULL;
	ret = sceKernelTryAllocateFpl(Np9660Fpl, &buf);
	if(ret < 0){
		ret_error = ret;
		goto error;
	}
	fpl_buffer = buf;

	buf += 0x8000; // 0x8000 is first logical sector
	ret = sub_00005130(buf, 0x8000);
	if(ret < 0){
		ret_error = ret;
		goto error;
	}

	buf += 512;
	ret = sub_00000DA8(buf, 512);
	if(ret < 0){
		ret_error = ret;
		goto error;
	}

	Np9660NCMutex = sceKernelCreateMutex("SceNp9660NC", 0x201, 0, NULL);
	if(Np9660NCMutex < 0){
		ret_error = Np9660NCMutex;
		goto error;
	}

	int mutex_params[2] = { 8, 15 };
	Np9660Mutex = sceKernelCreateMutex("SceNp9660", 0x601, 0, mutex_params);
	if(Np9660Mutex < 0){
		ret_error = Np9660Mutex;
		goto error;
	}
	Np9660Mutex2 = Np9660Mutex;

	ret = sub_000065E4();
	if(ret < 0){
		ret_error = ret;
		goto error;
	}

	unk_i544.unk0 = 16;
	v0 = SysMemForKernel_E057A674(5, &unk_i544);
	unk_i544.unk1 &= 0x1FFFFFFF;

	return 0;

error:
	sub_0000671C();
	if(Np9660Mutex > 0){
		sceKernelDeleteMutex(Np9660Mutex);
		Np9660Mutex = 0;
	}
	sub_00000DD8();
	sub_00005160();
	if(Np9660Fpl > 0){
		sceKernelDeleteFpl(Np9660Fpl);
		Np9660Fpl = 0;
	}
	return ret_error;
}

// OK
// 0x00003ED4
int NP9660IoExit(PspIoDrvArg* arg){
	sub_0000671C();
	if(Np9660Mutex > 0){
		sceKernelDeleteMutex(Np9660Mutex);
		Np9660Mutex = 0;
	}
	if(Np9660NCMutex > 0){
		sceKernelDeleteMutex(Np9660NCMutex);
		Np9660NCMutex = 0;
	}
	if(Np9660Mutex2 > 0){
		sceKernelDeleteMutex(Np9660Mutex2);
		Np9660Mutex2 = 0;
	}

	memset(unk_i388, 0, 88);
	sub_00000DD8();
	sub_00005160();
	if(Np9660Fpl > 0){
		v0 = sceKernelDeleteFpl(Np9660Fpl);
		Np9660Fpl = 0;
	}

	return 0;
}

// OK
// 0x00003FA4
int NP9660IoOpen(PspIoDrvFileArg* arg, char* file, int flags, SceMode mode){
	if(( (arg<1) | (file<1) )){
		return SCE_ERROR_ERRNO_EINVAL;
	}
	if(flags & (PSP_O_WRONLY|PSP_O_APPEND|PSP_O_CREAT|PSP_O_TRUNC|PSP_O_EXCL)){
		return SCE_ERROR_ERRNO_EFLAG;
	}

	if( ((~(0 | flags)) & 0x1) == 0){
		if(arg->fs_num >= 2){
			return SCE_ERROR_ERRNO_EUNIT;
		}
		int intr = sceKernelCpuSuspendIntr();
		int i = 0;
		for(i=0; i<8; i++){
			if(files[i].opened == 0){
				files[i].opened = 1;
				if(flags & 0x4000){
					files[i].unk1 = 240;
				}else{
					files[i].unk1 = 0;
				}

				arg->arg = &files[i];
				arg->unk4 = 0;
				arg->unk4 = 0;
				sceKernelCpuResumeIntr(intr);
				return 0;
			}
		}
		sceKernelCpuResumeIntr(intr);
		return SCE_ERROR_ERRNO_EMFILE;

	}
	return SCE_ERROR_ERRNO_EFLAG;
}

// OK
// 0x000040A4
int NP9660IoClose(PspIoDrvFileArg* arg){
	if(arg == NULL){
		return SCE_ERROR_ERRNO_EINVAL;
	}

	int ret = 0;
	File* file = (File*)arg->arg;
	if(file){
		ret = SCE_ERROR_ERRNO_EBADF;
		int intr = sceKernelCpuSuspendIntr();
		if(file->opened){
			file->opened = 0;
			ret = 0;
		}
		sceKernelCpuResumeIntr(intr);
	}

	return ret;
}

// 
// 
// 0x00004268
int NP9660IoRead(PspIoDrvFileArg* arg, void* data, SceSize size){
	v0 = 0x80010000;
	v1 = v0 | 0x00000016;
	s0 = arg;
	s1 = data;
	s3 = size;
	s2 = File* file = (File*)arg->arg;

	if(arg == NULL){
		return SCE_ERROR_ERRNO_EINVAL;
	}
	if(arg->arg == NULL){
		goto SCE_ERROR_ERRNO_EINVAL;
	}

	a0 = Mem[s2];
	if(file->opened == 0){
		return SCE_ERROR_ERRNO_EBADF;
	}

	s4 = Mem[s0 + 24];
	v0 = sub_00005E54();
	a1 = s1;
	a0 = s4;
	a2 = s3;
	v0 = sub_0000434C(s4, );
	if(v0 <= 0){
		s1 = v0;
		goto loc_00004310;
	}
	s1 = v0
	a0 = Mem[s0 + 24];
	a1 = Mem[s0 + 28];
	a2 = v0 >> 31;
	a0 = a0 + v0;
	if(a0 < v0){
		t0 = 1;
	}else{
		t0 = 0;
	}
	a3 = a1 + a2;
	a1 = a3 + t0;
	Mem[s0 + 24] = a0;
	Mem[s0 + 28] = a1;
	v0 = sub_00005E80(24, 28, );
	a0 = s4;
	a1 = s1;
	if(v0 != 0){
		a2 = 0;
		goto loc_00004338;
	}
	a2 = 0

	//loc_00004310
	return s1;

	//loc_00004314
	//loc_00004318
	return v1;

	//loc_00004338
	v1 = Mem[s2 + 4];
	a3 = v1 & 0xF0;
	v0 = sub_0000595C();
	return s1;
}

SceOff NP9660IoLseek(PspIoDrvFileArg* arg, int a1, SceOff offset, int a3, int from){
	return 0x80020002;
}

// OK
// Maybe UnsetReadParams()
// 0x00005160
int sub_00005160(){
	unk_i564 = -1;
	return 0;
}

// OK
// 0x00005E54
u32 sub_00005E54(){
	SceInt64 ret = sceKernelGetSystemTimeWide();
	time = ret;
	return (u32)ret;
}

// OK
// Maybe SetupDecodeThread()
// 0x000065E4
int sub_000065E4(){
	int ret_error = 0;

	SceNp9660DecMbx = sceKernelCreateMbx("SceNp9660Dec", 1, 0);
	if(SceNp9660DecMbx < 0){
		ret_error = SceNp9660DecMbx;
		goto error;
	}

	SceNp9660DecEventFlag = sceKernelCreateEventFlag("SceNp9660Dec", 1, 0, 0);
	if(SceNp9660DecEventFlag < 0){
		ret_error = SceNp9660DecEventFlag;
		goto error;
	}

	SceNp9660DecThread = sceKernelCreateThread("SceNp9660Dec", SceNp9660Dec_thread, 0x70, 4096, PSP_THREAD_ATTR_NO_FILLSTACK|0x00000001, NULL);
	if(SceNp9660DecThread < 0){
		ret_error = SceNp9660DecThread;
		goto error;
	}

	int ret = sceKernelStartThread(SceNp9660DecThread, 0, NULL);
	if(ret < 0){
		ret_error = ret;
		goto error;
	}

	return 0;

error:
	if(SceNp9660DecThread > 0){
		sceKernelDeleteThread(SceNp9660DecThread);
		SceNp9660DecThread = 0;
	}
	if(SceNp9660DecEventFlag > 0){
		sceKernelDeleteEventFlag(SceNp9660DecEventFlag);
		SceNp9660DecEventFlag = 0;
	}
	if(SceNp9660DecMbx > 0){
		sceKernelDeleteMbx(SceNp9660DecMbx);
		SceNp9660DecMbx = 0;
	}

	return ret_error;
}

// OK
// Maybe UnsetDecodeThread()
// sub_0000671C
int sub_0000671C(){
	if(SceNp9660Dec > 0){
		if(v1 > 0){
			u32 msg[6];
			msg[0] = 0;
			msg[1] = 0;
			msg[2] = 0;
			msg[3] = 0;
			msg[4] = 0;
			msg[5] = 0;
			sceKernelSendMbx(SceNp9660DecMbx, msg);
			sceKernelWaitThreadEnd(SceNp9660Dec);
		}
	}

	if(SceNp9660Dec > 0){
		sceKernelDeleteThread(SceNp9660Dec);
		SceNp9660Dec = 0;
	}

	if(SceNp9660DecEventFlag > 0){
		sceKernelDeleteEventFlag(SceNp9660DecEventFlag);
		SceNp9660DecEventFlag = 0;
	}

	if(SceNp9660DecMbx > 0){
		v0 = sceKernelDeleteMbx(SceNp9660DecMbx);
		SceNp9660DecMbx = 0;
	}

	return 0;
}
